package org.egedede.processmanager.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.egedede.processmanager.ProcessDefinition;
import org.egedede.processmanager.ProcessManager;
import org.egedede.processmanager.ProcessPack;
import org.egedede.processmanager.events.ProcessListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultProcessManager implements ProcessManager {
	private static final Logger LOG = LoggerFactory.getLogger(DefaultProcessManager.class);

	private final List<ProcessDefinition> processes = new LinkedList<ProcessDefinition>();

	private final List<ProcessListener> defaultListeners = new LinkedList<ProcessListener>();

	public DefaultProcessManager() {
	}

	public DefaultProcessManager(final List<ProcessListener> defaultListeners) {
		this();
		this.defaultListeners.addAll(defaultListeners);
	}

	@Override
	public void cancel(final ProcessDefinition process) {
		try {
			LOG.debug("start=>Cancelling ");
			process.getProcess().getProcess().destroy();
			LOG.debug("end  =>Cancelling ");
		} catch (final Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void process(final ProcessPack pack) {

		final Collection<Callable<Object>> tasks = new ArrayList<Callable<Object>>();
		for (final ProcessDefinition def : pack.getPack()) {
			tasks.add(new Callable<Object>() {

				@Override
				public Object call() throws Exception {
					for (final ProcessListener l : defaultListeners) {
						def.addProcessListener(l);
					}
					processes.add(def);
					def.start();
					return def.getLabel();
				}

			});
		}
		switch (pack.getMode()) {
		case PARALLEL:
			final ExecutorService exec2 = Executors.newFixedThreadPool(tasks.size());
			try {
				exec2.invokeAll(tasks);
			} catch (final InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			break;
		case SEQUENTIAL:
			final ExecutorService exec = Executors.newFixedThreadPool(1);
			for (final Callable<Object> c : tasks) {
				try {
					exec.invokeAll(Collections.singletonList(c));
				} catch (final InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			break;
		}
	}

	@Override
	public void process(final ProcessDefinition def) {
		for (final ProcessListener l : defaultListeners) {
			def.addProcessListener(l);
		}
		def.start();
		processes.add(def);
	}

	@Override
	public List<ProcessDefinition> list() {
		return Collections.unmodifiableList(processes);
	}

	/**
	 * remove all finished process
	 */
	@Override
	public void clear() {
		for (final ProcessDefinition process : new LinkedList<ProcessDefinition>(processes)) {
			if (process.isFinished()) {
				processes.remove(process);
			}
		}
	}

	/**
	 * delete this process whatever it's status is.
	 */
	@Override
	public void delete(final ProcessDefinition process) {
		processes.remove(process);
	}

	@Override
	public void addListener(final ProcessListener listener) {
		defaultListeners.add(listener);
	}

	@Override
	public void removeListener(final ProcessListener listener) {
		defaultListeners.remove(listener);
	}
}